package ext.trace.geom;

import ext.trace.scene.Hit;
import ext.trace.scene.Scene;
import math.math;
import math.rand.RNG;
import math.region.Ray3;
import math.vec2;
import math.vec3;

public class Material
{
    
    public BRDF rBRDF;
    
    public double mRadExit;
    
    public double mKd;
    
    public double mKs;
    
    public double mReflAngle;
    
    
    public Material()
    {
        this.mRadExit = 0.0;
        this.mKd = 0.0;
        this.mKs = 0.0;
        this.mReflAngle = 0.0;
    }
    
    
    public final void set(Material aOther)
    {
        this.mRadExit = aOther.mRadExit;
        this.mKd = aOther.mKd;
        this.mKs = aOther.mKs;
        this.mReflAngle = aOther.mReflAngle;
    }
    
    
    public boolean equals(Material aOther)
    {
        boolean emiss = math.abs(this.mRadExit - aOther.mRadExit) < math.EPSILON;
        boolean kd = math.abs(this.mKd - aOther.mKd) < math.EPSILON;
        boolean ks = math.abs(this.mKs - aOther.mKs) < math.EPSILON;
        boolean reflAngle = math.abs(this.mReflAngle - aOther.mReflAngle) < math.EPSILON;
        return (emiss && kd && ks && reflAngle);
    }
    
    
    public vec3 shade(
            Ray3 aRay, 
            Hit aHit, 
            Scene aScene, 
            RNG aRNG)
    {
        vec3 surfNorm = aHit.rNorm;
        vec3 V = vec3.mul(aRay.mDir, -1.0);
        
        vec3 direct = new vec3();
        for (int i = 0; i < aScene.mLights.size(); i++)
        {
            Geom light = aScene.mLights.get(i);
            double lightArea = light.rSurf0.surfaceArea();
            
            vec2 lightUV = aRNG.rand2f();
            vec3 lightPt = light.rSurf0.convertParam_World(lightUV);
            vec3 L = vec3.sub(lightPt, aHit.rPt);
            double distSq = L.lengthSq();
            L.norm();
            
            double cos0 = L.dot(surfNorm);
            double cos1 = -L.dot(light.rSurf0.surfaceNorm(lightPt, aHit.rPt));
            if (cos0 < 0.0 || cos1 < 0.0)
            {
                continue;
            }
            
            Ray3 occlRay = new Ray3(aHit.rPt, L);
            double occl = aScene.castOcclusion(occlRay, light, 0.0);
            if (occl < 1.0)
            {
                continue;
            }
            
            vec3 BRDF = this.rBRDF.sample(aHit, L, V);
            vec3 lightCol = light.mMat.rBRDF.rAlbedo.lookupNN(lightUV);
            double lightCoeff = light.mMat.mRadExit * lightArea * cos0 * cos1 / distSq;
            
            vec3 contrib = vec3.mul(BRDF, lightCol);
            contrib.mul(lightCoeff);
            
            direct.add(contrib);
        }
        return direct;
    }
    
}
